<?php


namespace App\Service\Xunsearch;


require_once '/mnt/btpanel/xunsearch/sdk/php/lib/XS.php';

//require_once '/mnt/btpanel/xunsearch/sdk/php/lib/XSDocument.php';

class Xunsearch
{

    protected $xs;
    protected $doc;
    protected $range;
    protected $error;

    // 初始化
    public function __construct() { }

    // 返回错误信息
    public function getError()
    {
        return $this->error;
    }

    // 搜索
    public function search($query, $start = 0, $number = 10, $order = ['ctime' => 'desc'])
    {
        // 搜索语句
        // $search->setQuery("category:article AND (subject:{$keyword} OR content:{$keyword})")->addRange("id", null, $lastId - 1)->addRange("uid", 1, null);

        // 搜索语句一般是 OR AND 组合的字符串
        // 另外还有大于，小于，区间的数据
        // 还有多个可能性的in操作，使用 or

        // 判断是否选择数据库
        $res = $this->isCheckDb();
        if($res !== true){
            return false;
        }

        // 排序：默认id降序
        $search = $this->xs->search;
        $search = $search->setQuery($query);
        $sorts  = [];
        if($order){
            foreach($order as $_key => $_sort){
                $sorts[$_key] = $_sort == 'desc' ? false : true;
            }
            $search = $search->setMultiSort($sorts);
        }
        if($this->range){
            foreach($this->range as $_field => $_item){
                $search = $search->addRange($_field, $_item[0], $_item[1]);
            }
        }

        // 执行搜索 getFields
        $docs = $search->setLimit($number, $start)->search();
        $list = [];
        foreach($docs as $doc){
            // 其中常用魔术方法：percent() 表示匹配度百分比, rank() 表示匹配结果序号
            //echo $doc->rank().'. '.$doc->subject." [".$doc->percent()."%] - ";
            // echo date("Y-m-d", $doc->chrono)."\n".$doc->message."\n";
            $list[] = $doc->getFields();
        }

        return $list;
    }

    // 获取搜索数量（必须先使用search方法，在执行此方法）
    public function count()
    {
        // 判断是否选择数据库
        $res = $this->isCheckDb();
        if($res !== true){
            return false;
        }

        return $this->xs->search->getLastCount();
    }

    // 连贯操作（用于大于，小于，区间的操作）（区间包含大于与小于的那两个数的）
    public function addRange($field, $from = null, $to = null)
    {
        // 判断是否选择数据库
        $res = $this->isCheckDb();
        if($res !== true){
            return false;
        }

        if(empty($this->range)){
            $this->range[$field] = [$from, $to];
        } else{
            $this->range = array_merge($this->range, [$field => [$from, $to]]);
        }

        return $this;
    }

    // 设置数据库
    // 现在有三个数据库：news:文章、 fresh:讨论、 dispatch:快讯
    public function setDb($db)
    {
        // 自动使用 $prefix/sdk/php/app/slh.ini 作项目配置文件
        $this->xs  = new \XS('slh_'.$db);
        $this->doc = new \XSDocument();

        return $this;
    }

    // 获取热门关键词
    public function hot($number = 6)
    {
        // 判断是否选择数据库
        $res = $this->isCheckDb();
        if($res !== true){
            return false;
        }

        $words = $this->xs->search->getHotQuery($number);

        return $words;
    }

    // 添加：添加文档 添加所有 更新索引
    public function add($row)
    {
        // 判断是否选择数据库
        $res = $this->isCheckDb();
        if($res !== true){
            return false;
        }

        $xs  = $this->xs;
        $doc = $this->doc;

        $doc->setFields($row);
        $xs->index->add($doc);
        //（每次添加、修改、删除等操作都需要更新索引）
        $this->flush();

        return true;
    }


    // 修改：更新文档 更新索引 更新索引
    public function update($row)
    {
        // 判断是否选择数据库
        $res = $this->isCheckDb();
        if($res !== true){
            return false;
        }

        $xs  = $this->xs;
        $doc = $this->doc;

        $doc->setFields($row);
        $xs->index->update($doc);
        //（每次添加、修改、删除等操作都需要更新索引）
        $this->flush();

        return true;
    }


    // 删除：删除文档 删除索引 更新所有
    public function delete($archive_id)
    {
        // 判断是否选择数据库
        $res = $this->isCheckDb();
        if($res !== true){
            return false;
        }

        $xs = $this->xs;
        $xs->index->del($archive_id);
        //（每次添加、修改、删除等操作都需要更新索引）
        $this->flush();

        return true;
    }

    // 清空索引
    public function clean()
    {
        // 判断是否选择数据库
        $res = $this->isCheckDb();
        if($res !== true){
            return false;
        }

        $this->xs->index->clean();
    }

    // 批量添加索引
    public function batch($lists)
    {
        // 判断是否选择数据库
        $res = $this->isCheckDb();
        if($res !== true){
            return false;
        }

        $index = $this->xs->index;
        $doc   = $this->doc;

        // 宣布开始重建索引
        $index->beginRebuild();

        // 然后在此开始添加数据
        foreach($lists as $row){
            $doc->setFields($row);
            $index->add($doc);
        }

        // 告诉服务器重建完比
        $index->endRebuild();

        return true;
    }


    ##############################################################################################
    // 更新索引
    //（每次添加、修改、删除等操作都需要更新索引）
    protected function flush()
    {
        $this->xs->index->flushIndex();
        $this->xs->index->flushLogging();
    }

    // 判断是否选择数据库
    protected function isCheckDb()
    {
        if(empty($this->xs)){
            return $this->error = '请选择数据库';
        }

        return true;
    }
}
